﻿#include "ssWinCallStack.h"
#include <sstream>

#ifdef _WIN32
#define MAX_NAME_LENGTH 2048
namespace StarSeeker
{
	ssWinCallStack::ssWinCallStack()
	{
		Init();
	}
	void ssWinCallStack::Init()
	{
		m_hProcess = ::GetCurrentProcess();
	}

	bool ssWinCallStack::GetCallStack(std::string& out)
	{
		CONTEXT c;
		STACKFRAME64 s={ 0 };
		DWORD imageType = 0;
		IMAGEHLP_SYMBOL64 sym = { 0 };
		HANDLE hThread = GetCurrentThread();
		SymInitialize(m_hProcess, NULL, TRUE);
		if (hThread == GetCurrentThread())
		{
			GET_CURRENT_CONTEXT(c, USED_CONTEXT_FLAGS);
		}
		else
		{
			SuspendThread(hThread);
			memset(&c, 0, sizeof(CONTEXT));
			c.ContextFlags = USED_CONTEXT_FLAGS;
			if (GetThreadContext(hThread, &c) == FALSE)
			{
				ResumeThread(hThread);
				return false;
			}
		}
		CONTEXT* pContext = &c;

#ifdef _M_IX86
		// normally, call ImageNtHeader() and use machine info from PE header
		imageType = IMAGE_FILE_MACHINE_I386;
		s.AddrPC.Offset = c.Eip;
		s.AddrPC.Mode = AddrModeFlat;
		s.AddrFrame.Offset = c.Ebp;
		s.AddrFrame.Mode = AddrModeFlat;
		s.AddrStack.Offset = c.Esp;
		s.AddrStack.Mode = AddrModeFlat;
#elif _M_X64
		imageType = IMAGE_FILE_MACHINE_AMD64;
		s.AddrPC.Offset = c.Rip;
		s.AddrPC.Mode = AddrModeFlat;
		s.AddrFrame.Offset = c.Rsp;
		s.AddrFrame.Mode = AddrModeFlat;
		s.AddrStack.Offset = c.Rsp;
		s.AddrStack.Mode = AddrModeFlat;
#elif _M_IA64
		imageType = IMAGE_FILE_MACHINE_IA64;
		s.AddrPC.Offset = c.StIIP;
		s.AddrPC.Mode = AddrModeFlat;
		s.AddrFrame.Offset = c.IntSp;
		s.AddrFrame.Mode = AddrModeFlat;
		s.AddrBStore.Offset = c.RsBSP;
		s.AddrBStore.Mode = AddrModeFlat;
		s.AddrStack.Offset = c.IntSp;
		s.AddrStack.Mode = AddrModeFlat;
#else
#error "Platform not supported!"
#endif
		char symbolBuffer[sizeof(IMAGEHLP_SYMBOL64) + MAX_NAME_LENGTH];
		PIMAGEHLP_SYMBOL64 pSymbol = (PIMAGEHLP_SYMBOL64)symbolBuffer;
		IMAGEHLP_LINE64 lineInfo;
		//IMAGEHLP_MODULE64_V2 moduleInfo;
		IMAGEHLP_MODULE64 moduleInfo;
		DWORD symDisplacement = 0;
		DWORD dwLineDisplacement;
		std::ostringstream os;

		while (true)
		{
			if (!StackWalk64(imageType, m_hProcess, hThread, &s, pContext, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL))
			{
				break;
			}

			if (s.AddrFrame.Offset == 0)
			{
				break;
			}

			pSymbol->MaxNameLength = MAX_NAME_LENGTH;
			pSymbol->SizeOfStruct = sizeof(symbolBuffer);

			if (SymGetSymFromAddr64(m_hProcess, s.AddrPC.Offset, NULL, pSymbol))
			{
			}
			if (SymGetLineFromAddr64(m_hProcess, s.AddrPC.Offset, &dwLineDisplacement, &lineInfo))
			{
			}
			//moduleInfo.SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2);
			moduleInfo.SizeOfStruct = sizeof(IMAGEHLP_MODULE64);
			if (SymGetModuleInfo64(m_hProcess, s.AddrPC.Offset, &moduleInfo))
			{
			}
			os << std::hex << "[0x" << pSymbol->Address << "][" << std::dec << moduleInfo.ModuleName << "][" <<
				pSymbol->Name << "][" << lineInfo.FileName << ":" << lineInfo.LineNumber << "]" << std::endl;
		}
		out = os.str();

		SymCleanup(m_hProcess);
		return true;
	}
}
#endif
